/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imagepipeline.transcoder;

import com.facebook.common.logging.FLog;
import com.facebook.imageformat.DefaultImageFormats;
import com.facebook.imageformat.ImageFormat;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.common.RotationOptions;
import com.facebook.imagepipeline.image.EncodedImage;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.*;

import java.io.OutputStream;
import javax.annotation.Nullable;

/**
 * Image transcoder using only the openharmony API. Clients can use this if they don't want to use the
 * native implementation. This image transcoder requires more memory.
 */
public class SimpleImageTranscoder implements ImageTranscoder {
    private static final String TAG = "SimpleImageTranscoder";
    private final boolean mResizingEnabled;
    private final int mMaxBitmapSize;

    public SimpleImageTranscoder(final boolean resizingEnabled, final int maxBitmapSize) {
        mResizingEnabled = resizingEnabled;
        mMaxBitmapSize = maxBitmapSize;
    }

    @Override
    public ImageTranscodeResult transcode(
            EncodedImage encodedImage,
            OutputStream outputStream,
            @Nullable RotationOptions rotationOptions,
            @Nullable ResizeOptions resizeOptions,
            @Nullable ImageFormat outputFormat,
            @Nullable Integer quality) {
        if (quality == null) {
            quality = JpegTranscoderUtils.DEFAULT_JPEG_QUALITY;
        }
        if (rotationOptions == null) {
            rotationOptions = RotationOptions.autoRotate();
        }

        final int sampleSize = getSampleSize(encodedImage, rotationOptions, resizeOptions);
        final ImageSource.DecodingOptions options = new ImageSource.DecodingOptions();
        options.sampleSize = sampleSize;
        PixelMap resizedBitmap;
        try {
            ImageSource imageSource = ImageSource.create(encodedImage.getInputStream(), new ImageSource.SourceOptions());
            resizedBitmap = imageSource.createPixelmap(options);
        } catch (OutOfMemoryError oom) {
            FLog.e(TAG, "Out-Of-Memory during transcode", oom);
            return new ImageTranscodeResult(TranscodeStatus.TRANSCODING_ERROR);
        }

        if (resizedBitmap == null) {
            FLog.e(TAG, "Couldn't decode the EncodedImage InputStream ! ");
            return new ImageTranscodeResult(TranscodeStatus.TRANSCODING_ERROR);
        }

        Matrix transformationMatrix =
                JpegTranscoderUtils.getTransformationMatrix(encodedImage, rotationOptions);

        PixelMap srcBitmap = null;
        try {
            if (transformationMatrix != null) {
                int width = resizedBitmap.getImageInfo().size.width;
                int height = resizedBitmap.getImageInfo().size.height;
//              Rect rect = new Rect();
//              rect.minX = 0;
//              rect.minY = 0;
//              rect.width = resizedBitmap.getImageInfo().size.width;
//              rect.height = resizedBitmap.getImageInfo().size.height;
              PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();

//              srcBitmap = PixelMap.create(resizedBitmap, rect, initializationOptions);
                RectFloat srcR = new RectFloat(0, 0, width, height);
                RectFloat dstR = new RectFloat(0, 0, width, height);
                RectFloat deviceR = new RectFloat();
                Paint paint = new Paint();
                final boolean transformed = !transformationMatrix.rectStaysRect();
                if (transformed) {
                    paint.setAntiAlias(true);
                }
                transformationMatrix.mapRect(deviceR, dstR);

                int neww = Math.round(deviceR.getWidth());
                int newh = Math.round(deviceR.getHeight());
                initializationOptions.size = new Size(neww,newh);
                initializationOptions.scaleMode = ScaleMode.CENTER_CROP;
                initializationOptions.alphaType = AlphaType.OPAQUE;
                srcBitmap = PixelMap.create(initializationOptions);
                Texture texture = new Texture(srcBitmap);
                Canvas canvas = new Canvas(texture);

                canvas.translate(-deviceR.left, -deviceR.top);
                canvas.concat(transformationMatrix);
                canvas.drawPixelMapHolderRect(new PixelMapHolder(resizedBitmap), srcR, dstR, paint);

                ImagePacker packer = ImagePacker.create();
                boolean result = packer.initializePacking(outputStream,getOutputFormat(outputFormat,quality));
                result = packer.addImage(texture.getPixelMap());
                long size = packer.finalizePacking();
            }else {
                srcBitmap = resizedBitmap;
                ImagePacker packer = ImagePacker.create();
                boolean result = packer.initializePacking(outputStream,getOutputFormat(outputFormat,quality));
                result = packer.addImage(srcBitmap);
                long size = packer.finalizePacking();
            }


            return new ImageTranscodeResult(
                    sampleSize > DownsampleUtil.DEFAULT_SAMPLE_SIZE
                            ? TranscodeStatus.TRANSCODING_SUCCESS
                            : TranscodeStatus.TRANSCODING_NO_RESIZING);
        } catch (OutOfMemoryError oom) {
            FLog.e(TAG, "Out-Of-Memory during transcode", oom);
            return new ImageTranscodeResult(TranscodeStatus.TRANSCODING_ERROR);
        } finally {
            srcBitmap.release();
            resizedBitmap.release();
        }
    }

    @Override
    public boolean canResize(
            EncodedImage encodedImage,
            @Nullable RotationOptions rotationOptions,
            @Nullable ResizeOptions resizeOptions) {
        if (rotationOptions == null) {
            rotationOptions = RotationOptions.autoRotate();
        }
        return mResizingEnabled
                && DownsampleUtil.determineSampleSize(
                rotationOptions, resizeOptions, encodedImage, mMaxBitmapSize)
                > DownsampleUtil.DEFAULT_SAMPLE_SIZE;
    }

    @Override
    public boolean canTranscode(ImageFormat imageFormat) {
        return imageFormat == DefaultImageFormats.HEIF || imageFormat == DefaultImageFormats.JPEG;
    }

    @Override
    public String getIdentifier() {
        return "SimpleImageTranscoder";
    }

    private int getSampleSize(
            final EncodedImage encodedImage,
            final RotationOptions rotationOptions,
            @Nullable final ResizeOptions resizeOptions) {
        int sampleSize;
        if (!mResizingEnabled) {
            sampleSize = DownsampleUtil.DEFAULT_SAMPLE_SIZE;
        } else {
            sampleSize =
                    DownsampleUtil.determineSampleSize(
                            rotationOptions, resizeOptions, encodedImage, mMaxBitmapSize);
        }
        return sampleSize;
    }


   private static ImagePacker.PackingOptions getOutputFormat(@Nullable final ImageFormat format,int quality) {
       ImagePacker.PackingOptions options = new ImagePacker.PackingOptions();
       options.quality = quality;
        if (format == null) {
            options.format = "image/jpeg";

            return options;
        }

        if (format == DefaultImageFormats.JPEG) {
            options.format = "image/jpeg";

            return options;
        } else if (format == DefaultImageFormats.PNG) {
            options.format = "image/png";
            return options;
        } else {
            if (DefaultImageFormats.isStaticWebpFormat(format)) {
                options.format = "image/webp";
                return options;
            }
            options.format = "image/jpeg";
            return options;
        }
    }
}
